﻿#region Librerias

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ESF.Peer;
using System.Net;
using ESF.General;
using System.Net.Sockets;
using System.IO;
using ESF.General.Entity;
using ESF.General.Network;
using System.Text.RegularExpressions;
using System.Net.Mail;
using ESF.DNS.Helper;
using System.Threading.Tasks;
using System.Collections;

#endregion


namespace ESF.SMTPServer
{
	public class SMTPServer : BasePeer
	{
		#region Constantes

		public const string MAILFROM_REGEX = @"(?'cmdmailfrom'\s*MAIL\sFROM:){1}(\s*<?){0,1}(?'email'[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,6}){1}(>?){0,1}((\s*SIZE\=)(?'size'\d+)){0,1}";

		public const string RCPTO_REGEX = @"(?'cmdrcptto'\s*RCPT\sTO:){1}(\s*<?){0,1}(?'email'[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,6}){1}(>?){0,1}";

		public const string DATA_BOUNDARY_REGEX = @"(\s*boundary\s*=\s*"")(?'boundary'[\w|-]+)(\s*"")";

		#endregion


		#region Variables

		private List<SMTPRequest> _ClientRequests;

		private Queue _RequestQueue;

		private bool _IsDequeingMessages;

		#endregion


		#region Propiedades

		public string MainUserName
		{
			get
			{
				return ReadConfig.GetAppSetting ("MainUserName");
			}
		}


		public string MainUserPassword
		{
			get
			{
				return ReadConfig.GetAppSetting("MainUserPassword");
			}
		}


		public bool RequiresAuthentication
		{
			get
			{
				return ReadConfig.GetAppSettingBool ("RequiresAuthentication");
			}
		}


		public List<string> AllowedFromDomains
		{
			get
			{
				return TextHelper.Split (',', ReadConfig.GetAppSetting ("AllowedFromDomains"));
			}
		}

		#endregion


		#region Constructor

		public SMTPServer () 
			: base ("SMTPServer") 
		{
			_ClientRequests = new List<SMTPRequest> ();

			_RequestQueue = Queue.Synchronized (new Queue()); 
		}

		#endregion


		#region Requests

		protected override void OnReceive(Socket pRequestSocket, Stream pData)
		{
			SMTPRequest currentRequest;
			 
			int indexCRLF;

						 
			#region Read Command
			
			currentRequest = _ClientRequests.First (sr => sr.ClientEndPoint == pRequestSocket.RemoteEndPoint);

			// Se recibe texto como comandos
			if (currentRequest.CurrentStep != SMTPSteps.DATA)
			{
				pData.Position = currentRequest.StreamLastPosition;

				currentRequest.LastCommand = new StreamReader(pData).ReadToEnd();
				
				indexCRLF = currentRequest.LastCommand.IndexOf (Environment.NewLine);

				if (indexCRLF == -1)
				{
					return;
				}

				currentRequest.LastCommand = currentRequest.LastCommand.Substring(0, indexCRLF).Trim();
				
				if (indexCRLF + 2 >= pData.Length)
				{
					pData.Position = indexCRLF + 2;
				}

				currentRequest.StreamLastPosition = pData.Position;

				Log.WriteLineDebug ("RECEIVE: {0}", currentRequest.LastCommand);
			}

			#endregion

			
			// Si es un retorno de carro se omite a no ser que haga parte del mensaje
			if (currentRequest.CurrentStep != SMTPSteps.DATA && string.IsNullOrWhiteSpace (currentRequest.LastCommand))
			{
				return;
			}

			#region Basic Errors

			// Vienen backspaces en el comando
			if (currentRequest.LastCommand.Contains ('\b'))
			{
				SendBadSintaxErrorMessage (pRequestSocket, "Request contains \\b (backspaces)");
				return;
			}
			
			#endregion


			#region QUIT
			
			if (currentRequest.LastCommand.StartsWith ("QUIT", StringComparison.InvariantCultureIgnoreCase))
			{
				SendDisconnectMessage (pRequestSocket);
				pRequestSocket.Close ();

				return;
			}
						
			#endregion

			#region NOOP

			if (currentRequest.LastCommand.StartsWith("NOOP", StringComparison.InvariantCultureIgnoreCase))
			{
				SendOK (pRequestSocket);
				return;
			}

			#endregion

			#region VRFY

			if (currentRequest.LastCommand.StartsWith ("VRFY", StringComparison.InvariantCultureIgnoreCase))
			{
				SendLine (pRequestSocket, "252 2.1.5 VRFY user accounts is not supported by security reasons");
				return;
			}

			#endregion
			
			#region HELP
			
			if (currentRequest.LastCommand.StartsWith("HELP", StringComparison.InvariantCultureIgnoreCase))
			{
				SendHelpMessage(pRequestSocket);
				return;
			}
			
			#endregion

			#region RSET

			if (currentRequest.LastCommand.StartsWith("RSET", StringComparison.InvariantCultureIgnoreCase))
			{
				Clear (currentRequest, pData);

				SendLine (pRequestSocket, "250 Resetting");
				return;
			}

			#endregion

			#region HELO o EHLO

			// El primer request debe ser un HELO alias o ip maquina cliente
			// Ejemplo: HELO 192.168.1.200
			else if (currentRequest.CurrentStep == SMTPSteps.START)
			{
				if (currentRequest.LastCommand.StartsWith("HELO", StringComparison.InvariantCultureIgnoreCase)
					|| currentRequest.LastCommand.StartsWith("EHLO", StringComparison.InvariantCultureIgnoreCase))
				{
					currentRequest.CurrentStep = SMTPSteps.HELO_EHLO;

					Process_HELO_EHLO (currentRequest, pRequestSocket);
					return;
				}
				else
				{
					SendLine(pRequestSocket, "530 Client was not authenticated");
					return;
				}
			}

			#endregion
						
			#region AUTH

			// Se reciben los datos de autenticacion en plano (PLAIN)
			else if (currentRequest.LastCommand.StartsWith("AUTH", StringComparison.InvariantCultureIgnoreCase))
			{
				Process_AUTH (currentRequest, pRequestSocket);
				return;
			} 
			else if (currentRequest.CurrentStep == SMTPSteps.AUTH_LOGIN_UserName)
			{
				Process_AUTH_UserName(currentRequest, pRequestSocket);
				return;
			}
			else if (currentRequest.CurrentStep == SMTPSteps.AUTH_LOGIN_UserPassword)
			{
				Process_AUTH_UserPassword(currentRequest, pRequestSocket);
				return;
			}
			else if (currentRequest.CurrentStep == SMTPSteps.AUTH_OK)
			{
				#region MAIL
				
				if (currentRequest.LastCommand.StartsWith ("MAIL", StringComparison.InvariantCultureIgnoreCase))
				{
					Process_MAILFROM (currentRequest, pRequestSocket);
					return;
				}

				#endregion
			}
			else if (currentRequest.CurrentStep == SMTPSteps.MAIL_FROM_OK)
			{
				#region RCPTTO

				if (currentRequest.LastCommand.StartsWith("RCPT", StringComparison.InvariantCultureIgnoreCase))
				{
					Process_RCPTTO(currentRequest, pRequestSocket);
					return;
				}
				else if (currentRequest.LastCommand.StartsWith("DATA", StringComparison.InvariantCultureIgnoreCase))
				{
					Process_DATA (currentRequest, pRequestSocket);
					return;
				}

				#endregion
			}
			else if (currentRequest.CurrentStep == SMTPSteps.DATA)
			{
				Process_DATAContents (currentRequest, pRequestSocket, pData);
				return;
			}
			
			#endregion

			if (RequiresAuthentication && !currentRequest.IsAuthenticated)
			{
				SendAuthenticationRequiredMessage(pRequestSocket);
				return;
			}


			// TODO: Poner los demas comandos o sino 
			#region Comando no reconocido

				else
				{
					SendBadCommandMessage(pRequestSocket);
				}

				#endregion
		}


		private void Process_HELO_EHLO (SMTPRequest currentRequest, Socket pRequestSocket)
		{
			string clientHostName;

			IPAddress tmpIP;

			StringBuilder features = new StringBuilder ();


			clientHostName = currentRequest.LastCommand.Substring(4).Trim ();

			if (IPAddress.TryParse (clientHostName, out tmpIP) || clientHostName.Contains (" "))
			{
				SendBadSintaxErrorMessage(pRequestSocket, string.Format("Invalid hostname or IP: {0}. Use HELO [clientHostName] or EHLO [clientHostName]", clientHostName));
				return;
			}

			currentRequest.UsesESMTP = currentRequest.LastCommand.StartsWith ("EHLO", StringComparison.InvariantCultureIgnoreCase);

			currentRequest.HELO_ClientName = clientHostName;


			#region Features

			features.AppendFormat ("250-{0} Hello {1}", Dns.GetHostName(), currentRequest.ClientEndPoint.Address);
			features.AppendLine ();

			if (RequiresAuthentication)
			{
				// TODO: Agregar soporte a otros metodos NTLM, LOGIN, MD5 etc.
				features.AppendFormat("250-AUTH=LOGIN");
				features.AppendLine();
				features.AppendFormat("250-AUTH LOGIN");
				features.AppendLine();
			} 
			
			features.AppendFormat("250-SIZE {0}", MaxFileReceiveSizeBytes);
			features.AppendLine();
			features.AppendFormat("250-VRFY");
			features.AppendLine();
			features.AppendFormat("250-NOOP");
			features.AppendLine();
			features.AppendFormat("250-QUIT");
			features.AppendLine();
			features.AppendFormat("250 OK");
			features.AppendLine();

			Send (pRequestSocket, features.ToString ());

			#endregion
		}


		private void Process_AUTH (SMTPRequest currentRequest, Socket pRequestSocket)
		{
			string authMethod;


			authMethod = currentRequest.LastCommand.Substring (5).Trim ();


			// TODO: Soportar otros metodos, por ahora LOGIN
			switch (authMethod.ToUpper ())
			{
				#region LOGIN
				
				case "LOGIN":
					
					currentRequest.CurrentStep = SMTPSteps.AUTH_LOGIN_UserName;

					SendLine (pRequestSocket, "334 VXNlcm5hbWU6");

				break;

				#endregion
					
				default:
					SendBadSintaxErrorMessage(pRequestSocket, "Only supports AUTH LOGIN");
				return;
			}
		}


		private void Process_AUTH_UserName(SMTPRequest currentRequest, Socket pRequestSocket)
		{
			currentRequest.IsAuthenticated = false; 
			
			try
			{
				currentRequest.UserName = Encoding.UTF8.GetString (Convert.FromBase64String (currentRequest.LastCommand));
			}
			catch (Exception)
			{
				currentRequest.CurrentStep = SMTPSteps.HELO_EHLO; 
				
				SendBadSintaxErrorMessage(pRequestSocket, "Invalid Username format");
				return;
			}

			currentRequest.CurrentStep = SMTPSteps.AUTH_LOGIN_UserPassword;

			SendLine (pRequestSocket, "334 UGFzc3dvcmQ6");
		}


		private void Process_AUTH_UserPassword(SMTPRequest currentRequest, Socket pRequestSocket)
		{
			currentRequest.IsAuthenticated = false;

			try
			{
				currentRequest.UserPassword = Encoding.UTF8.GetString(Convert.FromBase64String(currentRequest.LastCommand));
			}
			catch (Exception)
			{
				currentRequest.CurrentStep = SMTPSteps.HELO_EHLO; 
				
				SendBadSintaxErrorMessage(pRequestSocket, "Invalid Password format");
				return;
			}

			if (currentRequest.UserName == MainUserName 
				&& currentRequest.UserPassword == MainUserPassword)
			{
				currentRequest.CurrentStep = SMTPSteps.AUTH_OK;

				currentRequest.IsAuthenticated = true;

				SendLine(pRequestSocket, "235 2.7.0 Authentication successful");
			}
			else
			{
				currentRequest.CurrentStep = SMTPSteps.HELO_EHLO;

				SendLine(pRequestSocket, "535 Authentication failed");
			}
		}


		private void Process_MAILFROM (SMTPRequest currentRequest, Socket pRequestSocket)
		{
			Regex cmdRegEx;

			Match resultRegEx;

			string mailDomain;

			
			currentRequest.CurrentStep = SMTPSteps.AUTH_OK;

			cmdRegEx = new Regex (MAILFROM_REGEX, RegexOptions.IgnoreCase);

			resultRegEx = cmdRegEx.Match (currentRequest.LastCommand);

			if (!resultRegEx.Success)
			{
				SendBadSintaxErrorMessage (pRequestSocket, "Use MAIL FROM:<email> SIZE=####");
				return;
			}

			currentRequest.MAIL_From = resultRegEx.Groups ["email"].Value;
			currentRequest.MAIL_Size = TextHelper.GetInt (resultRegEx.Groups["size"].Value);
			
			if (currentRequest.MAIL_Size > MaxFileReceiveSizeBytes)
			{
				SendLine (pRequestSocket, "552 Requested mail action aborted: exceeded storage allocation");
				return;
			}

			mailDomain = currentRequest.MAIL_From.Split ('@') [1];

			if (!TextHelper.IsIn(mailDomain, AllowedFromDomains))
			{
				SendBadSintaxErrorMessage (pRequestSocket, string.Format ("Mail from domain is not allowed: {0}", mailDomain));
				return;
			}

			currentRequest.CurrentStep = SMTPSteps.MAIL_FROM_OK;

			SendLine (pRequestSocket, string.Format ("250 {0}...Sender OK", currentRequest.MAIL_From));
		}


		private void Process_RCPTTO (SMTPRequest currentRequest, Socket pRequestSocket)
		{
			Regex cmdRegEx;

			Match resultRegEx;

			string emailTo;


			cmdRegEx = new Regex(RCPTO_REGEX, RegexOptions.IgnoreCase);

			resultRegEx = cmdRegEx.Match(currentRequest.LastCommand);

			if (!resultRegEx.Success)
			{
				SendBadSintaxErrorMessage(pRequestSocket, "Use RCPT TO:<email>");
				return;
			}

			emailTo = resultRegEx.Groups ["email"].Value;

			if (!currentRequest.RCPTTO_Tos.Contains(emailTo))
			{
				currentRequest.RCPTTO_Tos.Add(emailTo);
			}

			SendLine (pRequestSocket, string.Format ("250 {0}", emailTo));
		}


		private void Process_DATA (SMTPRequest currentRequest, Socket pRequestSocket)
		{
			if (DataHelper.IsEmpty (currentRequest.RCPTTO_Tos))
			{
				SendLine (pRequestSocket, "503 Need Rcpt command (add at least one email of destination).");
				return;
			}
		
			currentRequest.CurrentStep = SMTPSteps.DATA;

			SendLine(pRequestSocket, "354 Send message content; end with <CRLF>.<CRLF>");
		}


		// Se recibe el stream de datos que contiene cosas como (subject del correo, datos de los adjuntos): 
		// Message-ID: <50BFB1C0.1030209@sispro.gov.co>\r\nDate: Wed, 05 Dec 2012 15:42:40 -0500\r\nFrom: Test Email <test@sispro.gov.co>\r\nUser-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:17.0) Gecko/17.0 Thunderbird/17.0\r\nMIME-Version: 1.0\r\nTo: destinatario1@asdf.com, destinatario2@asdf.com\r\nSubject: ASUNTOOO\r\nContent-Type: multipart/mixed;\r\n boundary=\"------------060208080101090007040206\"\r\n\r\nThis is a multi-part message in MIME format.\r\n--------------060208080101090007040206\r\nContent-Type: text/plain; charset=ISO-8859-1; format=flowed\r\nContent-Transfer-Encoding: 8bit\r\n\r\nCuerpo\r\nCuer�pazo\r\n\r\n\r\najka���\r\n\r\n--------------060208080101090007040206\r\nContent-Type: image/jpeg;\r\n name=\"maldondo.jpg\"\r\nContent-Transfer-Encoding: base64\r\nContent-Disposition: attachment;\r\n filename=\"maldondo.jpg\"\r\n\r\n/9j/4fvBRXhpZgAATU0AKgAAAAgACQEPAAIAAAAIAAAAegEQAAIAAAAKAAAAggESAAMAAAAB\r\nAAEAAAEaAAUAAAABAAAAjAEbAAUAAAABAAAAlAEoAAMAAAABAAIAAAEyAAIAAAAyAAAAnAIT\r\nAAMAAAABAAEAAIdpAAQAAAABAAAAzgAAAl5TQU1TVU5HAEdULUk1NTEwTAAAAABIAAAAAQAA\r\nAEgAAAABMjAxMi0x
		private void Process_DATAContents (SMTPRequest currentRequest, Socket pRequestSocket, Stream pData)
		{
			Regex cmdRegEx;

			Match resultRegEx;

			string textContents;

			List<string> tmpMultiparts;


			// Se leen como texto y se pasan a un StringBuilder todos los contenidos buscando al final un punto y un CRLF
			// Si es la primera vez se ubica la posicion del stream en la ultima lectura de comando para proceder a sacar lo enviado en el buffer
			// y luego limpiarlo para seguir pasando del buffer al StringBuilder
			if (currentRequest.DATA_MessageIsBeginning)
			{
				currentRequest.DATA_MessageIsBeginning = false;
				
				pData.Position = currentRequest.StreamLastPosition;

				currentRequest.DATA_Descriptors = TextHelper.Split(pData, Environment.NewLine, ':');

				Log.WriteLineDebug("RECEIVE DataDescription:\n{0}", TextHelper.ToString(currentRequest.DATA_Descriptors));

				currentRequest.StreamLastPosition = pData.Position;
			}
			else
			{
				pData.Position = 0;
			}
			
			if (currentRequest.DATA_IsTextPlain)
			{
				textContents = new StreamReader (pData).ReadToEnd ();

				currentRequest.DATA_TextMessage.Append (textContents);

				Log.WriteLineDebug (textContents);

				
				if (textContents.EndsWith ("." + Environment.NewLine))
				{
					currentRequest.CurrentStep = SMTPSteps.DATA_Complete;

					Delivery (currentRequest, pRequestSocket, pData);
				}
			}
			else if (currentRequest.DATA_IsMultipartMixed || currentRequest.DATA_IsMultipartAlternative)
			{
				textContents = new StreamReader(pData).ReadToEnd ();

				Log.WriteLineDebug(textContents);

				if (string.IsNullOrEmpty (currentRequest.DATA_MultipartBoundary))
				{
					cmdRegEx = new Regex (DATA_BOUNDARY_REGEX, RegexOptions.IgnoreCase);

					resultRegEx = cmdRegEx.Match(textContents);

					if (!resultRegEx.Success)
					{
						SendBadSintaxErrorMessage (pRequestSocket, "Boundary text mark not found");
						return;
					}

					currentRequest.DATA_MultipartBoundary = resultRegEx.Groups["boundary"].Value;

					textContents = textContents.
						Substring (resultRegEx.Length);

					textContents = textContents.
						Substring(textContents.IndexOf(currentRequest.DATA_MultipartBoundary) + currentRequest.DATA_MultipartBoundary.Length);
				}


				// Se ha recibido contenido y es posible que en este este parte de una seccion del mensaje que falta por recibir y debe ser combinado y partido
				if (currentRequest.DATA_Multiparts.Count > 0)
				{
					textContents = currentRequest.DATA_Multiparts[currentRequest.DATA_Multiparts.Count - 1] + textContents;

					currentRequest.DATA_Multiparts.RemoveAt (currentRequest.DATA_Multiparts.Count - 1);
				}
				

				tmpMultiparts = TextHelper.Split(currentRequest.DATA_MultipartBoundary, textContents);

				// Hay contenido
				if (tmpMultiparts.Count > 0)
				{
					// Es parte de uno anterior
					if (!tmpMultiparts.First ().StartsWith ("Content-Type"))
					{
						currentRequest.DATA_Multiparts [currentRequest.DATA_Multiparts.Count - 1] += tmpMultiparts.First ();
												
						tmpMultiparts.RemoveAt (0);
					}
					
					foreach (string multipart in tmpMultiparts)
					{
						if (multipart.EndsWith (Environment.NewLine + "."))
						{
							Delivery (currentRequest, pRequestSocket, pData);
							return;
						}
						else
						{
							currentRequest.DATA_Multiparts.Add (multipart);
						}
					}
				}
			}
			else
			{
				SendBadSintaxErrorMessage (pRequestSocket, "Message Content Type is not defined");
			}
			
			DataHelper.Clear (pData);
		}


		private void Clear (SMTPRequest pCurrentRequest, Stream pData)
		{
			pCurrentRequest = new SMTPRequest ();

			DataHelper.Clear (pData);
		}


		private void Delivery (SMTPRequest pCurrentRequest, Socket pRequestSocket, Stream pData)
		{
			_RequestQueue.Enqueue (pCurrentRequest);

			SendLine(pRequestSocket, "250 Queued mail for delivery");

			SendOK (pRequestSocket);

			Clear (pCurrentRequest, pData);
		}

		#endregion


		#region Funciones de la Cola de Mensajes

		private void ProcessQueueMessage ()
		{
			SMTPRequest currentRequest;
			
			SmtpClient smtpClient;
			
			MailMessage message;

			MailAddress toEmail;

			Answer dnsMX;


			while (_RequestQueue.Count > 0)
			{
				_IsDequeingMessages = true;

				currentRequest = _RequestQueue.Dequeue () as SMTPRequest;
			
				message = new MailMessage();

				message.Subject = currentRequest.DATA_Subject;
				message.From = new MailAddress(currentRequest.MAIL_From);

				if (currentRequest.DATA_IsTextPlain)
				{
					message.Body = currentRequest.DATA_TextMessage.ToString ();
				}
				else
				{
					message.Body = "This is the message body multi";

					// TODO: Body y attachments
				}

				foreach (string to in currentRequest.RCPTTO_Tos)
				{
					toEmail = new MailAddress (to);
					
					message.To.Clear ();

					message.To.Add (toEmail);

					dnsMX = DNSHelper.GetMXFirstRecord(toEmail.Host);

					if (dnsMX == null)
					{
						// TODO: Registrar que no fue hallado el MX
						continue;
					}
					else
					{
						smtpClient = new System.Net.Mail.SmtpClient (dnsMX.Domain);
					}

					try
					{
						smtpClient.Send(message);
					}
					catch (Exception pEx)
					{
						// TODO: registrar y determinar que hacer segun el error

						Log.Error (pEx);
					}
				}
			}

			_IsDequeingMessages = false;
		}

		#endregion


		#region Timer Functions

		protected override void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
		{
			if (!_IsDequeingMessages && _RequestQueue.Count > 0)
			{
				ProcessQueueMessage ();
			}
		}

		#endregion


		#region Error Messages

		private void SendDisconnectMessage (Socket pRequetsSocket)
		{
			SendLine (pRequetsSocket, string.Format ("221 {0} Service closing transmission channel", Dns.GetHostName ()));
		}


		private void SendAuthenticationRequiredMessage(Socket pRequestSocket)
		{
			SendLine(pRequestSocket, "555 Authentication Required (Use AUTH)");
		}


		private void SendAccessDeniedMessage(Socket pRequestSocket)
		{
			SendLine(pRequestSocket, "530 Access denied");
		}


		private void SendBadSintaxErrorMessage (Socket pRequestSocket, string pDetails)
		{
			SendLine(pRequestSocket, string.Format("501 Syntax error in parameters or arguments: {0}", pDetails));
		}


		private void SendBadCommandMessage(Socket pRequestSocket)
		{
			SendLine(pRequestSocket, "500 Syntax error, command unrecognised");
		}


		private void SendHelpMessage(Socket pRequestSocket)
		{
			SendLine(pRequestSocket, "214 This server supports the following commands:");
			SendLine(pRequestSocket, "214 HELO EHLO RCPT DATA RSET MAIL QUIT HELP AUTH VRFY");// STARTTLS 
		}


		private void SendOK(Socket pRequestSocket)
		{
			SendLine(pRequestSocket, "250 OK");
		}

		#endregion


		#region Client Request Events

		protected override void OnClientConnection(Socket pRequestSocket)
		{
			string temp;


			_ClientRequests.RemoveAll (cr => cr.ClientEndPoint == (IPEndPoint) pRequestSocket.RemoteEndPoint);

			_ClientRequests.Add(new SMTPRequest() { ClientEndPoint = (IPEndPoint) pRequestSocket.RemoteEndPoint });

			if (!IPAddress.Any.Equals (ReceiverIPEndPoint.Address) 
				&& !IPAddress.IPv6Any.Equals (ReceiverIPEndPoint.Address)
				&& !IPAddress.Loopback.Equals(ReceiverIPEndPoint.Address))
			{
				temp = Dns.GetHostEntry (ReceiverIPEndPoint.Address).HostName;
			}
			else
			{
				temp = "Server (peer)";
			}


			SendLine(pRequestSocket, string.Format("220 {0} {1}, version {2} - ready at {3}", 
				temp, 
				(string.IsNullOrWhiteSpace (ReadConfig.ApplicationName) ? Name : ReadConfig.ApplicationName), 
				ReadConfig.ExecutingVersion, 
				DateTime.Now.ToString (TextHelper.DEFAULT_DATETIME_FORMAT)));
		}


		protected override void OnClientDenied(Socket pRequestSocket)
		{
			SendLine(pRequestSocket, 
				string.Format ("530 Connection is not allowed for your machine at {0}", 
					pRequestSocket.RemoteEndPoint));
		}


		protected override void OnConnectionUnused(Socket pRequestSocket)
		{
			SendLine(pRequestSocket,
				string.Format("451 Timeout waiting for client input at {0}",
					pRequestSocket.RemoteEndPoint));
		}


		protected override void OnStopForMaxReceiveSize(Socket pRequestSocket)
		{
 			SendLine (pRequestSocket, "536 Max Size of request was reached");
		}

		#endregion
	}
}
